﻿/* ***********************************************
 * Author		:  catii
 * DateTime		:  2023-1008-28
 * Description	:  剔除密码加密（在TinyProxy模式下用不到，并且简化代码）
 *
 * ***********************************************/

using System.Net;
using System.Net.Sockets;
using System.Text;
using WinFormsApp.socks;

namespace org.catii.socks5;

/// <summary>
/// 本地sock5服务器
/// </summary>
public class RemoteServer {

    private TcpListener listener;
    private bool stoped = false;

    public ushort Port {
        get;
        private set;
    }

    /// <summary>
    /// RemoteServer
    /// </summary>
    /// <param name="port">端口号</param>
    public RemoteServer(ushort port) {
        Port = port;
        listener = new TcpListener(IPAddress.Any, this.Port);
        listener.Start();
        listener.BeginAcceptTcpClient(OnAcceptTcpClient, null);
    }

    /// <summary>
    /// 停止服务
    /// </summary>
    public void Stop() {
        try {
            if (!stoped) {
                stoped = true;
                listener.Stop();
            }
        } catch (Exception) {
            //ignored
        }
    }

    /// <summary>
    /// 接收连接
    /// </summary>
    /// <param name="async"></param>
    private void OnAcceptTcpClient(IAsyncResult async) {
        if (stoped) {
            return;
        }

        try {
            TcpClient tcpClient = listener.EndAcceptTcpClient(async);
            Console.WriteLine(string.Format("于{0}接收到{1}的连接请求...", DateTime.Now, tcpClient.Client.RemoteEndPoint));

            ThreadPool.QueueUserWorkItem(x => {
                try {
                    AcceptClient(x);
                } catch (Exception) {
                }
            }, tcpClient);

            //异步socket，继续接受其他客户端接入
            listener.BeginAcceptTcpClient(OnAcceptTcpClient, null);
        } catch (ObjectDisposedException) { } catch (Exception ex) {
            Console.WriteLine(string.Format("于{0}发生错误,错误信息:{1}", DateTime.Now, ex.Message));
        }
    }

    private void AcceptClient(object? state) {
        if (state is not TcpClient tcpClient || stoped) {
            return;
        }
        IPEndPoint? localEP = tcpClient.Client.LocalEndPoint as IPEndPoint;
        byte[]? localIP = localEP?.Address.GetAddressBytes();
        if (localIP == null || localEP == null) {
            return;
        }
        NetworkStream stream = tcpClient.GetStream();

        byte[] lenBytes = new byte[4];
        stream.Read(lenBytes, 0, 4);
        T.De(lenBytes);
        int len = BitConverter.ToInt32(lenBytes, 0);

        byte[] tokenBytes = new byte[len];
        stream.Read(tokenBytes, 0, len);
        String token = Encoding.UTF8.GetString(T.De0(tokenBytes), 0, len);
        //判断token，回复客户端用什么新的令牌

        //// Socks5 Shake Hand 
        byte[] buffer = new byte[258];
        int readVerNmethod = stream.Read(buffer, 0, 2);
        bool shakeFailed = false;
        if (readVerNmethod != 2 || buffer[0] != 0x05) {
            shakeFailed = true;
            Console.WriteLine("--本地代理只支持sock5协议版本！");
        }

        //跳过客户端请求的协议栈
        //VERSION	METHODS_COUNT	METHODS
        //1字节    1字节            1到255字节，长度由METHODS_COUNT值决定
        //0x05     0x03             0x00 0x01 0x02
        stream.Read(buffer, 0, buffer[1]);

        //无需认证
        //VERSION METHOD
        //1字节   1字节
        buffer[0] = 0x05;
        buffer[1] = 0x00;
        if (shakeFailed) {
            buffer[1] = 0xff;
        }
        stream.Write(buffer, 0, 2);
        if (shakeFailed) {
            return;
        }

        //不支持的地址类型
        byte rep = 0x08;

        //VERSION:1 COMMAND:1 RSV:1 ADDRESS_TYPE:1 DST.ADDR:1-255 DST.PORT:2
        IPAddress? ipAddress = null;
        if (stream.Read(buffer, 0, 4) == 4) {
            //判断地址类型
            if (buffer[3] == 0x01) {
                //IPV4
                byte[] ipV4 = new byte[4];
                stream.Read(ipV4, 0, 4);
                ipAddress = new IPAddress(ipV4);
            } else if (buffer[3] == 0x03) {
                //域名
                stream.Read(buffer, 0, 1); //域名长度
                int domainLen = stream.Read(buffer, 0, buffer[0]);
                string address = Encoding.ASCII.GetString(buffer, 0, domainLen);
                IPAddress[] addresses = Dns.GetHostAddresses(address);
                if (addresses.Length != 0) {
                    ipAddress = addresses[0];
                } else {
                    rep = 0x04;  //主机不可达
                }
            } else if (buffer[3] == 0x04) {
                //IPV6;
                byte[] ipV6 = new byte[16];
                stream.Read(ipV6);
                ipAddress = new IPAddress(buffer);
            }

        }

        if (ipAddress != null) {
            rep = 0x00;
        }

        //输出应答
        MemoryStream resp = new MemoryStream();
        resp.WriteByte(0x05);
        resp.WriteByte(rep);
        resp.WriteByte(0x00);
        resp.WriteByte(0x01);
        resp.Write(localIP, 0, localIP.Length);
        byte[] localPort = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder(localEP.Port));
        resp.Write(localPort, 0, localPort.Length);
        resp.Seek(0, SeekOrigin.Begin);
        resp.CopyTo(stream);

        if (ipAddress != null) {
            //取得端口号
            stream.Read(buffer, 0, 2);
            byte lo = buffer[0];
            buffer[0] = buffer[1];
            buffer[1] = lo;
            IPEndPoint endPoint = new(ipAddress, BitConverter.ToUInt16(buffer, 0));
            Console.WriteLine(string.Format("于{0}接收到客户端要求对主机{1}进行连接的请求....", DateTime.Now, endPoint));
            handleProxy(tcpClient, endPoint);
        }
    }

    private void handleProxy(TcpClient tcpClient, IPEndPoint endPoint) {
        TcpClient remote = new();
        remote.Connect(endPoint);
        NetworkStream clientStream = tcpClient.GetStream();
        NetworkStream remoteStream = remote.GetStream();
        PxyState state = new(clientStream, remoteStream);
        clientStream.BeginRead(state.bClient, 0, state.bClient.Length, ReadClient, state);
        remoteStream.BeginRead(state.bRemote, 0, state.bRemote.Length, ReadRemote, state);
    }

    private void ReadRemote(IAsyncResult ar) {
        if (ar.AsyncState is not State state || stoped) {
            return;
        }

        try {
            int read = state.RemoteStream.EndRead(ar);
            if (read > 0) {
                state.ClientStream.Write(state.bRemote, 0, read);
                state.RemoteStream.BeginRead(state.bRemote, 0, state.bRemote.Length, ReadRemote, state);
            }
        } catch (Exception) {
            state.Close();
        }
    }

    private void ReadClient(IAsyncResult ar) {
        if (ar.AsyncState is not PxyState state || stoped) {
            return;
        }

        try {
            int read = state.ClientStream.EndRead(ar);
            if (read > 0) {
                state.RemoteStream.Write(state.bClient, 0, read);
                state.ClientStream.BeginRead(state.bClient, 0, state.bClient.Length, ReadClient, state);
            }
        } catch (Exception) {
            state.Close();
        }
    }
}

class PxyState {
    public NetworkStream ClientStream { get; set; }
    public NetworkStream RemoteStream { get; set; }

    public readonly byte[] bClient = new byte[2048];

    public readonly byte[] bRemote = new byte[2048];

    public PxyState(NetworkStream client, NetworkStream remote) {
        ClientStream = client;
        RemoteStream = remote;
    }

    public void Close() {
        try {
            ClientStream.Close();
            RemoteStream.Close();
        } catch (Exception) {
            //ignored throw;
        }
    }
}

